/*
 * Copyright (c) 2011 Soren Dreijer
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */

/*
 * Copyright (c) 2012-2016 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swiften/TLS/Schannel/SchannelContext.h>

#include <boost/bind.hpp>

#include <WinHTTP.h> /* For SECURITY_FLAG_IGNORE_CERT_CN_INVALID */

#include <Swiften/TLS/CAPICertificate.h>
#include <Swiften/TLS/Schannel/SchannelCertificate.h>

namespace Swift {

//------------------------------------------------------------------------

SchannelContext::SchannelContext(bool tls1_0Workaround) : state_(Start), secContext_(0), myCertStore_(NULL), certStoreName_("MY"), certName_(), smartCardReader_(), checkCertificateRevocation_(true), tls1_0Workaround_(tls1_0Workaround), disconnectOnCardRemoval_(true) {
	contextFlags_ = ISC_REQ_ALLOCATE_MEMORY |
				ISC_REQ_CONFIDENTIALITY |
				ISC_REQ_EXTENDED_ERROR |
				ISC_REQ_INTEGRITY |
				ISC_REQ_REPLAY_DETECT |
				ISC_REQ_SEQUENCE_DETECT |
				ISC_REQ_USE_SUPPLIED_CREDS |
				ISC_REQ_STREAM;

	ZeroMemory(&streamSizes_, sizeof(streamSizes_));
}

//------------------------------------------------------------------------

SchannelContext::~SchannelContext() {
	if (myCertStore_) CertCloseStore(myCertStore_, 0);
}

//------------------------------------------------------------------------

void SchannelContext::determineStreamSizes() {
	QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_);
}

//------------------------------------------------------------------------

void SchannelContext::connect() {
	ScopedCertContext pCertContext;

	state_ = Connecting;

	// If a user name is specified, then attempt to find a client
	// certificate. Otherwise, just create a NULL credential.
	if (!certName_.empty()) {
		if (myCertStore_ == NULL) {
			myCertStore_ = CertOpenSystemStore(0, certStoreName_.c_str());
			if (!myCertStore_) {
				indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
				return;
			}
		}

		pCertContext = findCertificateInStore( myCertStore_, certName_ );
		if (pCertContext == NULL) {
			indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
			return;
		}
	}

	// We use an empty list for client certificates
	PCCERT_CONTEXT clientCerts[1] = {0};

	SCHANNEL_CRED sc = {0};
	sc.dwVersion = SCHANNEL_CRED_VERSION;

	if (tls1_0Workaround_) {
		sc.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
	}
	else {
		sc.grbitEnabledProtocols = /*SP_PROT_SSL3_CLIENT | */SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
	}

	sc.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;

	if (pCertContext) {
		sc.cCreds = 1;
		sc.paCred = pCertContext.GetPointer();
		sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
	}
	else {
		sc.cCreds = 0;
		sc.paCred = clientCerts;
		sc.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
	}

	// Swiften performs the server name check for us
	sc.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;

	SECURITY_STATUS status = AcquireCredentialsHandle(
		NULL,
		UNISP_NAME,
		SECPKG_CRED_OUTBOUND,
		NULL,
		&sc,
		NULL,
		NULL,
		credHandle_.Reset(),
		NULL);

	if (status != SEC_E_OK) {
		// We failed to obtain the credentials handle
		indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
		return;
	}

	SecBuffer outBuffers[2];

	// We let Schannel allocate the output buffer for us
	outBuffers[0].pvBuffer   = NULL;
	outBuffers[0].cbBuffer   = 0;
	outBuffers[0].BufferType = SECBUFFER_TOKEN;

	// Contains alert data if an alert is generated
	outBuffers[1].pvBuffer   = NULL;
	outBuffers[1].cbBuffer   = 0;
	outBuffers[1].BufferType = SECBUFFER_ALERT;

	// Make sure the output buffers are freed
	ScopedSecBuffer scopedOutputData(&outBuffers[0]);
	ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);

	SecBufferDesc outBufferDesc = {0};
	outBufferDesc.cBuffers   = 2;
	outBufferDesc.pBuffers   = outBuffers;
	outBufferDesc.ulVersion  = SECBUFFER_VERSION;

	// Create the initial security context
	status = InitializeSecurityContext(
		credHandle_,
		NULL,
		NULL,
		contextFlags_,
		0,
		0,
		NULL,
		0,
		contextHandle_.Reset(),
		&outBufferDesc,
		&secContext_,
		NULL);

	if (status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
		// We failed to initialize the security context
		handleCertError(status);
		indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
		return;
	}

	// Start the handshake
	sendDataOnNetwork(outBuffers[0].pvBuffer, outBuffers[0].cbBuffer);

	if (status == SEC_E_OK) {
		status = validateServerCertificate();
		if (status != SEC_E_OK) {
			handleCertError(status);
		}

		state_ = Connected;
		determineStreamSizes();

		onConnected();
	}
}

//------------------------------------------------------------------------

SECURITY_STATUS SchannelContext::validateServerCertificate() {
	SchannelCertificate::ref pServerCert = boost::dynamic_pointer_cast<SchannelCertificate>( getPeerCertificate() );
	if (!pServerCert) {
		return SEC_E_WRONG_PRINCIPAL;
	}

	const LPSTR usage[] =
	{
		szOID_PKIX_KP_SERVER_AUTH,
		szOID_SERVER_GATED_CRYPTO,
		szOID_SGC_NETSCAPE
	};

	CERT_CHAIN_PARA chainParams = {0};
	chainParams.cbSize = sizeof(chainParams);
	chainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
	chainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(usage);
	chainParams.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast<LPSTR*>(usage);

	DWORD chainFlags = CERT_CHAIN_CACHE_END_CERT;
	if (checkCertificateRevocation_) {
		chainFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
	}

	ScopedCertChainContext pChainContext;

	BOOL success = CertGetCertificateChain(
		NULL, // Use the chain engine for the current user (assumes a user is logged in)
		pServerCert->getCertContext(),
		NULL,
		pServerCert->getCertContext()->hCertStore,
		&chainParams,
		chainFlags,
		NULL,
		pChainContext.Reset());

	if (!success) {
		return GetLastError();
	}

	SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslChainPolicy = {0};
	sslChainPolicy.cbSize = sizeof(sslChainPolicy);
	sslChainPolicy.dwAuthType = AUTHTYPE_SERVER;
	sslChainPolicy.fdwChecks = SECURITY_FLAG_IGNORE_CERT_CN_INVALID; // Swiften checks the server name for us. Is this the correct way to disable server name checking?
	sslChainPolicy.pwszServerName = NULL;

	CERT_CHAIN_POLICY_PARA certChainPolicy = {0};
	certChainPolicy.cbSize = sizeof(certChainPolicy);
	certChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG; // Swiften checks the server name for us. Is this the correct way to disable server name checking?
	certChainPolicy.pvExtraPolicyPara = &sslChainPolicy;

	CERT_CHAIN_POLICY_STATUS certChainPolicyStatus = {0};
	certChainPolicyStatus.cbSize = sizeof(certChainPolicyStatus);

	// Verify the chain
	if (!CertVerifyCertificateChainPolicy(
		CERT_CHAIN_POLICY_SSL,
		pChainContext,
		&certChainPolicy,
		&certChainPolicyStatus)) {
		return GetLastError();
	}

	if (certChainPolicyStatus.dwError != S_OK) {
		return certChainPolicyStatus.dwError;
	}

	return S_OK;
}

//------------------------------------------------------------------------

void SchannelContext::appendNewData(const SafeByteArray& data) {
	size_t originalSize = receivedData_.size();
	receivedData_.resize(originalSize + data.size());
	memcpy(&receivedData_[0] + originalSize, &data[0], data.size());
}

//------------------------------------------------------------------------

void SchannelContext::continueHandshake(const SafeByteArray& data) {
	appendNewData(data);

	while (!receivedData_.empty()) {
		SecBuffer inBuffers[2];

		// Provide Schannel with the remote host's handshake data
		inBuffers[0].pvBuffer	 = (char*)(&receivedData_[0]);
		inBuffers[0].cbBuffer	 = (unsigned long)receivedData_.size();
		inBuffers[0].BufferType  = SECBUFFER_TOKEN;

		inBuffers[1].pvBuffer   = NULL;
		inBuffers[1].cbBuffer   = 0;
		inBuffers[1].BufferType = SECBUFFER_EMPTY;

		SecBufferDesc inBufferDesc = {0};
		inBufferDesc.cBuffers   = 2;
		inBufferDesc.pBuffers   = inBuffers;
		inBufferDesc.ulVersion  = SECBUFFER_VERSION;

		SecBuffer outBuffers[2];

		// We let Schannel allocate the output buffer for us
		outBuffers[0].pvBuffer   = NULL;
		outBuffers[0].cbBuffer   = 0;
		outBuffers[0].BufferType = SECBUFFER_TOKEN;

		// Contains alert data if an alert is generated
		outBuffers[1].pvBuffer   = NULL;
		outBuffers[1].cbBuffer   = 0;
		outBuffers[1].BufferType = SECBUFFER_ALERT;

		// Make sure the output buffers are freed
		ScopedSecBuffer scopedOutputData(&outBuffers[0]);
		ScopedSecBuffer scopedOutputAlertData(&outBuffers[1]);

		SecBufferDesc outBufferDesc = {0};
		outBufferDesc.cBuffers   = 2;
		outBufferDesc.pBuffers   = outBuffers;
		outBufferDesc.ulVersion  = SECBUFFER_VERSION;

		SECURITY_STATUS status = InitializeSecurityContext(
			credHandle_,
			contextHandle_,
			NULL,
			contextFlags_,
			0,
			0,
			&inBufferDesc,
			0,
			NULL,
			&outBufferDesc,
			&secContext_,
			NULL);

		if (status == SEC_E_INCOMPLETE_MESSAGE)	{
			// Wait for more data to arrive
			break;
		}
		else if (status == SEC_I_CONTINUE_NEEDED) {
			SecBuffer* pDataBuffer = &outBuffers[0];
			SecBuffer* pExtraBuffer = &inBuffers[1];

			if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) {
				sendDataOnNetwork(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
			}

			if (pExtraBuffer->BufferType == SECBUFFER_EXTRA) {
				receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer);
			}
			else {
				receivedData_.clear();
			}

			break;
		}
		else if (status == SEC_E_OK)  {
			status = validateServerCertificate();
			if (status != SEC_E_OK) {
				handleCertError(status);
			}

			SecBuffer* pExtraBuffer = &inBuffers[1];

			if (pExtraBuffer && pExtraBuffer->cbBuffer > 0) {
				receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer);
			}
			else {
				receivedData_.clear();
			}

			state_ = Connected;
			determineStreamSizes();

			onConnected();
		}
		else {
			// We failed to initialize the security context
			handleCertError(status);
			indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
			return;
		}
	}
}

//------------------------------------------------------------------------

void SchannelContext::handleCertError(SECURITY_STATUS status)
{
	if (status == SEC_E_UNTRUSTED_ROOT			||
		status == CERT_E_UNTRUSTEDROOT			||
		status == CRYPT_E_ISSUER_SERIALNUMBER	||
		status == CRYPT_E_SIGNER_NOT_FOUND		||
		status == CRYPT_E_NO_TRUSTED_SIGNER) {
		verificationError_ = CertificateVerificationError::Untrusted;
	}
	else if (status == SEC_E_CERT_EXPIRED ||
			 status == CERT_E_EXPIRED) {
		verificationError_ = CertificateVerificationError::Expired;
	}
	else if (status == CRYPT_E_SELF_SIGNED) {
		verificationError_ = CertificateVerificationError::SelfSigned;
	}
	else if (status == CRYPT_E_HASH_VALUE	||
			 status == TRUST_E_CERT_SIGNATURE) {
		verificationError_ = CertificateVerificationError::InvalidSignature;
	}
	else if (status == CRYPT_E_REVOKED) {
		verificationError_ = CertificateVerificationError::Revoked;
	}
	else if (status == CRYPT_E_NO_REVOCATION_CHECK ||
			 status == CRYPT_E_REVOCATION_OFFLINE) {
		verificationError_ = CertificateVerificationError::RevocationCheckFailed;
	}
	else if (status == CERT_E_WRONG_USAGE) {
		verificationError_ = CertificateVerificationError::InvalidPurpose;
	}
	else {
		verificationError_ = CertificateVerificationError::UnknownError;
	}
}

//------------------------------------------------------------------------

void SchannelContext::sendDataOnNetwork(const void* pData, size_t dataSize) {
	if (dataSize > 0 && pData) {
		SafeByteArray byteArray(dataSize);
		memcpy(&byteArray[0], pData, dataSize);

		onDataForNetwork(byteArray);
	}
}

//------------------------------------------------------------------------

void SchannelContext::forwardDataToApplication(const void* pData, size_t dataSize) {
	SafeByteArray byteArray(dataSize);
	memcpy(&byteArray[0], pData, dataSize);

	onDataForApplication(byteArray);
}

//------------------------------------------------------------------------

void SchannelContext::handleDataFromApplication(const SafeByteArray& data) {
	// Don't attempt to send data until we're fully connected
	if (state_ == Connecting) {
		return;
	}

	// Encrypt the data
	encryptAndSendData(data);
}

//------------------------------------------------------------------------

void SchannelContext::handleDataFromNetwork(const SafeByteArray& data) {
	switch (state_) {
	case Connecting:
		{
			// We're still establishing the connection, so continue the handshake
			continueHandshake(data);
		}
		break;

	case Connected:
		{
			// Decrypt the data
			decryptAndProcessData(data);
		}
		break;

	default:
		return;
	}
}

//------------------------------------------------------------------------

void SchannelContext::indicateError(boost::shared_ptr<TLSError> error) {
	state_ = Error;
	receivedData_.clear();
	onError(error);
}

//------------------------------------------------------------------------

void SchannelContext::decryptAndProcessData(const SafeByteArray& data) {
	SecBuffer inBuffers[4]	= {0};

	appendNewData(data);

	while (!receivedData_.empty()) {
		//
		// MSDN:
		//   When using the Schannel SSP with contexts that are not connection oriented, on input,
		//   the structure must contain four SecBuffer structures. Exactly one buffer must be of type
		//   SECBUFFER_DATA and contain an encrypted message, which is decrypted in place. The remaining
		//   buffers are used for output and must be of type SECBUFFER_EMPTY. For connection-oriented
		//   contexts, a SECBUFFER_DATA type buffer must be supplied, as noted for nonconnection-oriented
		//   contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token
		//   must also be supplied.
		//
		inBuffers[0].pvBuffer	 = (char*)(&receivedData_[0]);
		inBuffers[0].cbBuffer	 = (unsigned long)receivedData_.size();
		inBuffers[0].BufferType  = SECBUFFER_DATA;

		inBuffers[1].BufferType  = SECBUFFER_EMPTY;
		inBuffers[2].BufferType  = SECBUFFER_EMPTY;
		inBuffers[3].BufferType  = SECBUFFER_EMPTY;

		SecBufferDesc inBufferDesc = {0};
		inBufferDesc.cBuffers      = 4;
		inBufferDesc.pBuffers      = inBuffers;
		inBufferDesc.ulVersion     = SECBUFFER_VERSION;

		size_t inData = receivedData_.size();
		SECURITY_STATUS status = DecryptMessage(contextHandle_, &inBufferDesc, 0, NULL);

		if (status == SEC_E_INCOMPLETE_MESSAGE) {
			// Wait for more data to arrive
			break;
		}
		else if (status == SEC_I_RENEGOTIATE) {
			// TODO: Handle renegotiation scenarios
			indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
			break;
		}
		else if (status == SEC_I_CONTEXT_EXPIRED) {
			indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
			break;
		}
		else if (status != SEC_E_OK) {
			indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
			break;
		}

		SecBuffer* pDataBuffer = NULL;
		SecBuffer* pExtraBuffer = NULL;
		for (int i = 0; i < 4; ++i) {
			if (inBuffers[i].BufferType == SECBUFFER_DATA) {
				pDataBuffer = &inBuffers[i];
			}
			else if (inBuffers[i].BufferType == SECBUFFER_EXTRA) {
				pExtraBuffer = &inBuffers[i];
			}
		}

		if (pDataBuffer && pDataBuffer->cbBuffer > 0 && pDataBuffer->pvBuffer != NULL) {
			forwardDataToApplication(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);
		}

		// If there is extra data left over from the decryption operation, we call DecryptMessage() again
		if (pExtraBuffer) {
			receivedData_.erase(receivedData_.begin(), receivedData_.end() - pExtraBuffer->cbBuffer);
		}
		else {
			// We're done
			receivedData_.erase(receivedData_.begin(), receivedData_.begin() + inData);
		}
	}
}

//------------------------------------------------------------------------

void SchannelContext::encryptAndSendData(const SafeByteArray& data) {
	if (streamSizes_.cbMaximumMessage == 0) {
		return;
	}

	SecBuffer outBuffers[4]	= {0};

	// Calculate the largest required size of the send buffer
	size_t messageBufferSize = (data.size() > streamSizes_.cbMaximumMessage)
							 ? streamSizes_.cbMaximumMessage
							 : data.size();

	// Allocate a packet for the encrypted data
	SafeByteArray sendBuffer;
	sendBuffer.resize(streamSizes_.cbHeader + messageBufferSize + streamSizes_.cbTrailer);

	size_t bytesSent = 0;
	do {
		size_t bytesLeftToSend = data.size() - bytesSent;

		// Calculate how much of the send buffer we'll be using for this chunk
		size_t bytesToSend = (bytesLeftToSend > streamSizes_.cbMaximumMessage)
						   ? streamSizes_.cbMaximumMessage
						   : bytesLeftToSend;

		// Copy the plain text data into the send buffer
		memcpy(&sendBuffer[0] + streamSizes_.cbHeader, &data[0] + bytesSent, bytesToSend);

		outBuffers[0].pvBuffer	 = &sendBuffer[0];
		outBuffers[0].cbBuffer	 = streamSizes_.cbHeader;
		outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;

		outBuffers[1].pvBuffer	 = &sendBuffer[0] + streamSizes_.cbHeader;
		outBuffers[1].cbBuffer	 = (unsigned long)bytesToSend;
		outBuffers[1].BufferType = SECBUFFER_DATA;

		outBuffers[2].pvBuffer	 = &sendBuffer[0] + streamSizes_.cbHeader + bytesToSend;
		outBuffers[2].cbBuffer	 = streamSizes_.cbTrailer;
		outBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER;

		outBuffers[3].pvBuffer   = 0;
		outBuffers[3].cbBuffer   = 0;
		outBuffers[3].BufferType = SECBUFFER_EMPTY;

		SecBufferDesc outBufferDesc = {0};
		outBufferDesc.cBuffers   = 4;
		outBufferDesc.pBuffers   = outBuffers;
		outBufferDesc.ulVersion  = SECBUFFER_VERSION;

		SECURITY_STATUS status = EncryptMessage(contextHandle_, 0, &outBufferDesc, 0);
		if (status != SEC_E_OK) {
			indicateError(boost::make_shared<TLSError>(TLSError::UnknownError));
			return;
		}

		sendDataOnNetwork(&sendBuffer[0], outBuffers[0].cbBuffer + outBuffers[1].cbBuffer + outBuffers[2].cbBuffer);
		bytesSent += bytesToSend;

	} while (bytesSent < data.size());
}

//------------------------------------------------------------------------

bool SchannelContext::setClientCertificate(CertificateWithKey::ref certificate) {
	boost::shared_ptr<CAPICertificate> capiCertificate = boost::dynamic_pointer_cast<CAPICertificate>(certificate);
	if (!capiCertificate || capiCertificate->isNull()) {
		return false;
	}

	userCertificate_ = capiCertificate;

	// We assume that the Certificate Store Name/Certificate Name
	// are valid at this point
	certStoreName_ = capiCertificate->getCertStoreName();
	certName_ = capiCertificate->getCertName();
////At the moment this is only useful for logging:
	smartCardReader_ = capiCertificate->getSmartCardReaderName();

	capiCertificate->onCertificateCardRemoved.connect(boost::bind(&SchannelContext::handleCertificateCardRemoved, this));

	return true;
}

//------------------------------------------------------------------------
void SchannelContext::handleCertificateCardRemoved() {
	if (disconnectOnCardRemoval_) {
		indicateError(boost::make_shared<TLSError>(TLSError::CertificateCardRemoved));
	}
}

//------------------------------------------------------------------------

std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const {
	std::vector<Certificate::ref> certificateChain;
	ScopedCertContext pServerCert;
	ScopedCertContext pIssuerCert;
	ScopedCertContext pCurrentCert;
	SECURITY_STATUS status = QueryContextAttributes(contextHandle_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());

	if (status != SEC_E_OK) {
		return certificateChain;
	}
	certificateChain.push_back(boost::make_shared<SchannelCertificate>(pServerCert));

	pCurrentCert = pServerCert;
	while(pCurrentCert.GetPointer()) {
		DWORD dwVerificationFlags = 0;
		pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore, pCurrentCert, NULL, &dwVerificationFlags );
		if (!(*pIssuerCert.GetPointer())) {
			break;
		}
		certificateChain.push_back(boost::make_shared<SchannelCertificate>(pIssuerCert));

		pCurrentCert = pIssuerCert;
		pIssuerCert = NULL;
	}
	return certificateChain;
}

//------------------------------------------------------------------------

CertificateVerificationError::ref SchannelContext::getPeerCertificateVerificationError() const {
	return verificationError_ ? boost::make_shared<CertificateVerificationError>(*verificationError_) : CertificateVerificationError::ref();
}

//------------------------------------------------------------------------

ByteArray SchannelContext::getFinishMessage() const {
	SecPkgContext_Bindings bindings;
	int ret = QueryContextAttributes(contextHandle_, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings);
	if (ret == SEC_E_OK) {
		return createByteArray(((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset + 11 /* tls-unique:*/, bindings.Bindings->cbApplicationDataLength - 11);
	}
	return ByteArray();
}

//------------------------------------------------------------------------

void SchannelContext::setCheckCertificateRevocation(bool b) {
	checkCertificateRevocation_ = b;
}

void SchannelContext::setDisconnectOnCardRemoval(bool b) {
	disconnectOnCardRemoval_ = b;
}


}
